﻿#include <iostream>
//条款11：在operator=中处理"自我赋值"


//如果两个对象来自同一个继承结构，甚至不需要将他们声明为同一类型，因为基类引用或者指针可以引用或指向派生类类型的对象。
class Base{};
class Derived : public Base
{

};
void doSomething(const Base& rb,Derived* pd)
{
    //rb和pd可能是同一个对象
};

//如果试图自己管理资源，可能会陷入这样的陷阱：在使用完资源之前不小心释放资源。
class Bitmap{};
class Widget
{
public:
    void swap(Widget& rhs) // 交换两个Widget对象的pb指针
    {

    }
    Widget& operator=(const Widget& rhs)
    {
        //rhs可能是就是this
        delete pb;
        pb = new Bitmap(*rhs.pb); // 可能会在delete之前释放pb
        return *this;
        //*this(赋值的目标)和rhs可以是同一个对象，导致最终指向一个已经被删除的对象。

        //为了防止这种错误，传统方法是在方法顶部检查是否对自己赋值
        if(this == &rhs) return *this; // 自我赋值检查

        //它还有异常安全性的问题：如果"new Bitmap(*rhs.pb)"表达式产生异常（因为没有足够的内存用于分配，或者因为Bitmap的拷贝构造函数抛出异常），将最终指向已删除的Bitmap对象。
        //幸运的是，是operator=具有异常安全性，通常也会使它具有自赋值安全性。

        Bitmap* pOrig = pb; // 先保存原始指针
        pb = new Bitmap(*rhs.pb); // 让pb指向*rhs.pb的副本
        delete pOrig; // 删除原始Bitmap对象
        return *this; // 返回对赋值对象的引用
        //即使没有自我复制测试，上面的代码也具有自我赋值的安全性，因为创建了原始Bitmap对象的副本，删除了原始Bitmap,然后指向创建的副本。这不是最高效的方法，但有效。
        //除了手动排序operator=中的语句，以确保实现异常安全和自我赋值安全，另一种选择是使用称为"拷贝和交换"的技术。
        Widget temp(rhs); // 创建一个临时对象
        swap(temp); // 交换临时对象和当前对象的pb指针
        return *this; // 返回对赋值对象的引用


    }
    // Widget& operator=(Widget rhs) // 移动赋值运算符
    // {
    //     //类的复制赋值操作符可以声明为按值接收实参，按值传递某项内容会生成该实参的副本。
    //     swap(rhs);
    //     return *this; // 返回对赋值对象的引用
    // }
private:
    Bitmap* pb;
};
void item11()
{
    Widget w;
    w = w; // 自我赋值
}

//当对象被赋值给自身时，确保operator=表现良好。技术途径包括：比较源对象和目标对象的地址、仔细的语句排序、copy-and-swap技术。
//确保在两个或多个对象相同（是同一个对象）的情况下，函数的行为仍然正确。